#define GETTEXT_PACKAGE "gsmon"
#include <glib/gi18n-lib.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gprintf.h>
#include "gsm-view.h"
#include "gsm-marshals.h"

#define GSM_VERSION "0.6.2"

#ifdef NDEBUG
	#define GSM_PATH_ICONS "../share/gsmon/icons/"
	#define GSM_PATH_UI "../share/gsmon/ui/"
#else
	#define GSM_PATH_ICONS "./icons/"
	#define GSM_PATH_UI "./ui/"
#endif

enum {
	COL_SL_NAME,
	COL_SL_GAME_ICON,
	COL_SL_GAME,
	COL_SL_MAP,
	COL_SL_PLAYERS,
	COL_SL_PLAYERS_COLOR,
	COL_SL_PING,
	COL_SL_PING_COLOR,
	COL_SL_WATCHER,
	COL_SL_NUMS
};

enum {
	COL_FL_NAME,
	COL_FL_NOTIFY,
	COL_FL_NUMS
};

enum {
	SERVER_ADD,
	SERVER_REMOVE,
	ADDRESS_CHANGE,
	PREFERENCE_CHANGE,
	FRIEND_ADD,
	FRIEND_UPDATE,
	FRIEND_REMOVE,
	FRIEND_NAME_CHANGE,
	LAST_SIGNAL
};


static void gsm_view_dispose (GObject *object);

static void gsm_view_address_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_players_list_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_server_info_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_servers_list_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_tray_icon_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_friends_list_init (GsmView *view, GtkBuilder *builder);
static void gsm_view_prefs_init (GsmView *view, GtkBuilder *builder);

static guint signals[LAST_SIGNAL] = { 0 };


G_DEFINE_TYPE (GsmView, gsm_view, GTK_TYPE_WINDOW)

static void
gsm_view_class_init (GsmViewClass *klass)
{
	GObjectClass* gobject_class = (GObjectClass *) klass;
	gobject_class->dispose = gsm_view_dispose;
	
	signals[SERVER_ADD] = g_signal_new ("server-add",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, server_add),
			NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
			1, G_TYPE_STRING);
	
	signals[SERVER_REMOVE] = g_signal_new ("server-remove",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, server_remove),
			NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE,
			1, G_TYPE_OBJECT);
	
	signals[ADDRESS_CHANGE] = g_signal_new ("address-change",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, address_change),
			NULL, NULL, gsm_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN,
			1, G_TYPE_STRING);
	
	signals[PREFERENCE_CHANGE] = g_signal_new ("preference-change",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, preference_change),
			NULL, NULL, gsm_marshal_VOID__STRING_POINTER, G_TYPE_NONE,
			2, G_TYPE_STRING, G_TYPE_POINTER);
	
	signals[FRIEND_ADD] = g_signal_new ("friend-add",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, friend_add),
			NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
			1, G_TYPE_STRING);
	
	signals[FRIEND_UPDATE] = g_signal_new ("friend-update",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, friend_update),
			NULL, NULL, gsm_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE,
			2, G_TYPE_STRING, G_TYPE_BOOLEAN);
	
	signals[FRIEND_REMOVE] = g_signal_new ("friend-remove",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, friend_remove),
			NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
			1, G_TYPE_STRING);
	
	signals[FRIEND_NAME_CHANGE] = g_signal_new ("friend-name-change",
			G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
			G_STRUCT_OFFSET (GsmViewClass, friend_name_change),
			NULL, NULL, gsm_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN,
			1, G_TYPE_STRING);
}

static void
gsm_view_pause_toggled (GtkToggleAction *toggleaction, GsmView *view)
{
	gboolean b = gtk_toggle_action_get_active (toggleaction);
	g_signal_emit (view, signals[PREFERENCE_CHANGE], 0, "pause", b);
	GtkWidget *image = gtk_image_new_from_stock (
			b ? GTK_STOCK_NO : GTK_STOCK_YES, GTK_ICON_SIZE_BUTTON);
	gtk_button_set_image (GTK_BUTTON (view->pause_button), image);
}

static void
gsm_view_init (GsmView* view)
{
	GError *error = NULL;
	view->builder = gtk_builder_new ();
	if (!gtk_builder_add_from_file (view->builder, GSM_PATH_UI "gsmon.glade", &error))
		g_error ("Cannot load interface: %s", error->message);
	gtk_builder_connect_signals (view->builder, view);
	
	gchar *title = g_strdup_printf ("%s v%s", _("Game Servers Monitor"), GSM_VERSION);
	gtk_window_set_title (GTK_WINDOW (view), title);
	g_free (title);
	
	GtkWidget *pause_img = gtk_image_new_from_stock (GTK_STOCK_YES,
			GTK_ICON_SIZE_BUTTON);
	view->pause_button = gtk_toggle_button_new_with_mnemonic (_("_Pause"));
	gtk_button_set_image (GTK_BUTTON (view->pause_button), pause_img);
	gtk_widget_show_all (GTK_WIDGET (view->pause_button));

	GtkNotebook *nb = GTK_NOTEBOOK (gtk_builder_get_object (view->builder, "Root"));
	gtk_notebook_set_action_widget (GTK_NOTEBOOK (nb), GTK_WIDGET (view->pause_button),
			GTK_PACK_END);
	
	gtk_window_set_default_size (GTK_WINDOW (view), 800, 600);
	gtk_window_set_icon_from_file (GTK_WINDOW (view), GSM_PATH_ICONS "gsmon.png", NULL);
	gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (nb));
	
	view->remove_server = GTK_BUTTON (gtk_builder_get_object (view->builder,
			"Remove Server"));
	GtkRcStyle* rcstyle = gtk_rc_style_new ();
	rcstyle->xthickness = rcstyle->ythickness = 0;
	gtk_widget_modify_style (GTK_WIDGET (view->remove_server), rcstyle);
	g_object_unref (rcstyle);
	
	gsm_view_address_init (view, view->builder);
	gsm_view_players_list_init (view, view->builder);
	gsm_view_server_info_init (view, view->builder);
	gsm_view_servers_list_init (view, view->builder);
	gsm_view_tray_icon_init (view, view->builder);
	gsm_view_friends_list_init (view, view->builder);
	gsm_view_prefs_init (view, view->builder);
	
	view->pause_action = gtk_toggle_action_new ("Pause", _("_Pause"), NULL, NULL);
	gtk_activatable_set_related_action (GTK_ACTIVATABLE (view->pause_button),
			GTK_ACTION (view->pause_action));
	gtk_activatable_set_related_action (GTK_ACTIVATABLE (view->tray_menu_pause),
			GTK_ACTION (view->pause_action));
	g_signal_connect (view->pause_action, "toggled",
			G_CALLBACK (gsm_view_pause_toggled), view);
}

GtkWidget *
gsm_view_new ()
{
	return g_object_new (GSM_TYPE_VIEW, NULL);
}

static void
gsm_view_dispose (GObject *object)
{
	GsmView *view = GSM_VIEW (object);
	g_object_unref (view->builder);

	if (G_OBJECT_CLASS (gsm_view_parent_class)->dispose)
		G_OBJECT_CLASS (gsm_view_parent_class)->dispose (object);
}


// Address line   ==============================================================

static void
gsm_view_address_init (GsmView *view, GtkBuilder *builder)
{
	view->address = GTK_ENTRY (gtk_builder_get_object (builder, "Server's Address"));
}

void G_MODULE_EXPORT
gsm_view_address_icon_press (GtkEntry *entry, GtkEntryIconPosition icon_pos,
		GdkEvent *event, GsmView *view)
{
	if (icon_pos == GTK_ENTRY_ICON_SECONDARY) {
		g_signal_emit (view, signals[SERVER_ADD], 0, gtk_entry_get_text (entry));
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	}
}

gboolean G_MODULE_EXPORT
gsm_view_address_key_press (GtkEntry *entry, GdkEventKey *event, GsmView *view)
{
	if (event->keyval == GDK_Return) {
		g_signal_emit (view, signals[SERVER_ADD], 0, gtk_entry_get_text (entry));
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	}
	
	return FALSE;
}

void G_MODULE_EXPORT
gsm_view_address_changed (GtkEntry *entry, GsmView *view)
{
	gboolean exists = FALSE;
	const gchar* address = gtk_entry_get_text (entry);
	
	if (*address == 0)
		exists = TRUE;
	else
		g_signal_emit (view, signals[ADDRESS_CHANGE], 0, address, &exists);
	
	if (exists)
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	else
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, TRUE);
}


// Players list   ==============================================================

static void
gsm_view_players_list_init (GsmView *view, GtkBuilder *builder)
{
	GtkCellRenderer *cell;
	GtkTreeViewColumn *column;
	
	view->players_list = GTK_TREE_VIEW (gtk_builder_get_object (builder,
			"Players List"));
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("#"), cell,
			"text", 0, NULL);
	gtk_tree_view_column_set_sort_column_id (column, 0);
	gtk_tree_view_insert_column (view->players_list, column, -1);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Nick name"), cell,
			"text", 1, NULL);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, 1);
	gtk_tree_view_insert_column (view->players_list, column, -1);
}

static void
gsm_view_players_list_setup (GsmView *view, GsmWatcher *watcher)
{
	GtkTreeView *tree = view->players_list;
	GtkTreeViewColumn *column;
	GtkListStore *model;
	
// clear
	GList *cols, *icol;
	cols = gtk_tree_view_get_columns (tree);
	icol = g_list_next (g_list_next (cols));
	
	for (; icol; icol = g_list_next (icol))
		gtk_tree_view_remove_column (tree, icol->data);
	g_list_free (cols);
	
	model = GTK_LIST_STORE (gtk_tree_view_get_model (tree));
	if (model) {
		gtk_list_store_clear (model);
		gtk_tree_view_set_model (tree, NULL);
	}
	
// setup
	GsmPlayerParameter *params;
	GtkCellRenderer *cell;
	gint c = 0, i;
	
	if (!watcher)
		return;
	
	params = watcher->params;
	
	while (params[c].name) {
		cell = gtk_cell_renderer_text_new ();
		column = gtk_tree_view_column_new_with_attributes (params[c].name,
				cell, "text", c + 2, NULL);
		gtk_tree_view_column_set_sort_column_id (column, c + 2);
		gtk_tree_view_insert_column (tree, column, -1);
		c++;
	}
	
	c += 2;
	GType *types = g_new (GType, c);
	types[0] = G_TYPE_INT;
	types[1] = G_TYPE_STRING;
	
	for (i = 2; i < c; i++)
		types[i] = params[i - 2].type;
	
	model = gtk_list_store_newv (i, types);
	gtk_tree_view_set_model (tree, GTK_TREE_MODEL (model));
	g_object_unref (model);
	g_free (types);
}

static void
gsm_view_players_list_update (GsmView *view, GsmWatcher *watcher)
{
	GtkTreeModel *model;
	GtkTreeSelection *sel;
	GtkTreeIter iter;
	gchar *selected_name = NULL;
	GValue *value;
	int i = 1, j;
	GSList *list;
	
	// save a selected name
	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->players_list));
	if (gtk_tree_selection_get_selected (sel, &model, &iter))
		gtk_tree_model_get (model, &iter, 1, &selected_name, -1);
	
	gtk_list_store_clear (GTK_LIST_STORE (model));
	
	for (list = watcher->players; list; list = g_slist_next (list), i++) {
		GsmPlayer *player = list->data;
		
		gtk_list_store_append (GTK_LIST_STORE (model), &iter);
		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
				0, i,
				1, player->name,
				-1);
		for (value = player->values, j = 2; value->g_type != G_TYPE_INVALID;
				value++, j++)
			gtk_list_store_set_value (GTK_LIST_STORE (model), &iter, j, value);
		
		if (selected_name && strcmp (selected_name, player->name) == 0)
			gtk_tree_selection_select_iter (sel, &iter);
	}
	
	if (selected_name)
		g_free (selected_name);
}


// Server info   ===============================================================

static void
gsm_view_server_info_init (GsmView *view, GtkBuilder *builder)
{
	view->address_label = GTK_LABEL (gtk_builder_get_object (builder, "Address"));
	view->name_label = GTK_LABEL (gtk_builder_get_object (builder, "Name"));
	view->game_label = GTK_LABEL (gtk_builder_get_object (builder, "Game"));
	view->map_label = GTK_LABEL (gtk_builder_get_object (builder, "Map"));
	view->players_label = GTK_LABEL (gtk_builder_get_object (builder, "Players"));
	view->version_label = GTK_LABEL (gtk_builder_get_object (builder, "Version"));
}

static void
gsm_view_server_info_clear (GsmView *view)
{
	gtk_label_set_text (view->address_label, NULL);
	gtk_label_set_text (view->name_label, NULL);
	gtk_label_set_text (view->game_label, NULL);
	gtk_label_set_text (view->map_label, NULL);
	gtk_label_set_text (view->players_label, NULL);
	gtk_label_set_text (view->version_label, NULL);
}

static void
gsm_view_server_info_update (GsmView *view, GsmWatcher *watcher)
{
	gchar players[32];
	
	gtk_label_set_text (view->address_label, watcher->address);
	gtk_label_set_text (view->name_label, watcher->name);
	gtk_label_set_text (view->game_label, watcher->game);
	gtk_label_set_text (view->map_label, watcher->map);
	g_sprintf (players, "%d / %d", watcher->num_players, watcher->max_players);
	gtk_label_set_text (view->players_label, players);
	gtk_label_set_text (view->version_label, watcher->version);
}


// Servers list   ==============================================================

static void
gsm_view_servers_list_selection_changed (GtkTreeSelection *selection, GsmView *view)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GsmWatcher *watcher;
	
	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter, COL_SL_WATCHER, &watcher, -1);
		view->selected = watcher;
		
		gtk_entry_set_text (view->address, watcher->address);
		gsm_view_server_info_update (view, watcher);
		gsm_view_players_list_setup (view, watcher);
		gsm_view_players_list_update (view, watcher);
	} else {
		gtk_entry_set_text (view->address, "");
		gsm_view_server_info_clear (view);
		gsm_view_players_list_setup (view, NULL);
	}
}

static void
gsm_view_servers_list_init (GsmView *view, GtkBuilder *builder)
{
	GtkListStore *store;
	GtkCellRenderer *cell;
	GtkTreeViewColumn *column;
	GtkTreeSelection *sel;
	
	view->servers_list = GTK_TREE_VIEW (gtk_builder_get_object (builder,
			"Servers List"));
	sel = gtk_tree_view_get_selection (view->servers_list);
	g_signal_connect (sel, "changed",
			G_CALLBACK (gsm_view_servers_list_selection_changed), view);
	
	store = gtk_list_store_new (COL_SL_NUMS, G_TYPE_STRING, GDK_TYPE_PIXBUF,
			G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
			G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COL_SL_NAME,
			GTK_SORT_ASCENDING);
	gtk_tree_view_set_model (view->servers_list, GTK_TREE_MODEL (store));
	g_object_unref (store);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Name"), cell,
			"text", COL_SL_NAME, NULL);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, COL_SL_NAME);
	gtk_tree_view_append_column (view->servers_list, column);
	
	column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_title (column, _("Game"));
	cell = gtk_cell_renderer_pixbuf_new ();
	gtk_tree_view_column_pack_start (column, cell, FALSE);
	gtk_tree_view_column_set_attributes (column, cell,
			"pixbuf", COL_SL_GAME_ICON,
			NULL);
	cell = gtk_cell_renderer_text_new ();
	gtk_tree_view_column_pack_start (column, cell, TRUE);
	gtk_tree_view_column_set_attributes (column, cell,
			"text", COL_SL_GAME,
			NULL);
	gtk_tree_view_column_set_sort_column_id (column, COL_SL_GAME);
	gtk_tree_view_append_column (view->servers_list, column);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Map"), cell,
			"text", COL_SL_MAP,
			NULL);
	gtk_tree_view_column_set_sort_column_id (column, COL_SL_MAP);
	gtk_tree_view_append_column (view->servers_list, column);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Players"), cell,
			"text", COL_SL_PLAYERS,
			"foreground", COL_SL_PLAYERS_COLOR,
			NULL);
	gtk_tree_view_column_set_sort_column_id (column, COL_SL_PLAYERS);
	gtk_tree_view_append_column (view->servers_list, column);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Ping"), cell,
			"text", COL_SL_PING,
			"foreground", COL_SL_PING_COLOR,
			NULL);
	gtk_tree_view_column_set_sort_column_id (column, COL_SL_PING);
	gtk_tree_view_append_column (view->servers_list, column);
}

static void
gsm_view_servers_list_remove (GsmView *view)
{
	GtkTreeModel *model;
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	GsmWatcher *watcher;
	
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->servers_list));
	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter, COL_SL_WATCHER, &watcher, -1);
		GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (view),
				GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
				"Are you sure you want to remove server %s (%s) from list?",
				watcher->address, watcher->name);
		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) {
			g_signal_emit (view, signals[SERVER_REMOVE], 0, watcher);
			gtk_tree_selection_unselect_all (selection);
		}
		gtk_widget_destroy (dialog);
	}
}

void G_MODULE_EXPORT
gsm_view_servers_list_remove_clicked (GtkButton *button, GsmView *view)
{
	gsm_view_servers_list_remove (view);
}

gboolean G_MODULE_EXPORT
gsm_view_servers_list_key_press (GtkWidget *widget, GdkEventKey *event,
		GsmView *view)
{
	if (event->keyval == GDK_Delete)
		gsm_view_servers_list_remove (view);
	return FALSE;
}


// Status icon   ===============================================================

static void
gsm_view_tray_icon_init (GsmView *view, GtkBuilder *builder)
{
	view->tray_icon = GTK_STATUS_ICON (gtk_builder_get_object (builder, "TrayIcon"));
	gtk_status_icon_set_tooltip_text (view->tray_icon, _("Game Servers Monitor"));
	gtk_status_icon_set_from_file (view->tray_icon, GSM_PATH_ICONS "gsmon.png");
	
	view->tray_menu = GTK_MENU (gtk_builder_get_object (builder, "TrayMenu"));
	view->tray_menu_show = GTK_MENU_ITEM (gtk_builder_get_object (builder, "Show"));
	view->tray_menu_pause = GTK_MENU_ITEM (gtk_builder_get_object (builder, "Pause"));
}

gboolean G_MODULE_EXPORT
gsm_view_tray_icon_button_press (GtkStatusIcon *status_icon,
		GdkEventButton *event, GsmView *view)
{
	if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
		if (gtk_widget_get_visible (GTK_WIDGET (view)))
			gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view->tray_menu_show), FALSE);
		else
			gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view->tray_menu_show), TRUE);
	}
	
	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
		gtk_menu_popup (GTK_MENU (view->tray_menu), NULL, NULL,
#ifdef G_OS_WIN32
				NULL, NULL,
#else
				gtk_status_icon_position_menu, view->tray_icon,
#endif
				event->button, event->time);
	}
	
	return FALSE;
}

void G_MODULE_EXPORT
gsm_view_tray_icon_show_toggled (GtkCheckMenuItem *checkmenuitem, GsmView *view)
{
	if (gtk_check_menu_item_get_active (checkmenuitem)) {
		gtk_widget_show (GTK_WIDGET (view));
		gtk_window_deiconify (GTK_WINDOW (view));
	} else {
		gtk_widget_hide (GTK_WIDGET (view));
	}
}

void G_MODULE_EXPORT
gsm_view_tray_icon_exit_activate (GtkMenuItem *menuitem, GsmView *view)
{
	gtk_widget_destroy (GTK_WIDGET (view));
}


// Friends list   ==============================================================

static void
gsm_view_friends_list_selection_changed (GtkTreeSelection *selection,
		GsmView *view)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	gchar *name;
	
	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter, COL_FL_NAME, &name, -1);
		gtk_entry_set_text (view->friends_entry, name);
		g_free (name);
	} else {
		gtk_entry_set_text (view->friends_entry, "");
	}
}

static void
gsm_view_friends_list_init (GsmView *view, GtkBuilder *builder)
{
	GtkCellRenderer *cell;
	GtkTreeViewColumn *column;
	
	view->friends_entry = GTK_ENTRY (gtk_builder_get_object (builder,
			"Friend's Name"));
	
	view->remove_friend = GTK_BUTTON (gtk_builder_get_object (builder,
			"Remove Friend"));
	GtkRcStyle* rcstyle = gtk_rc_style_new ();
	rcstyle->xthickness = rcstyle->ythickness = 0;
	gtk_widget_modify_style (GTK_WIDGET (view->remove_friend), rcstyle);
	g_object_unref (rcstyle);
	
	view->friends_list = GTK_TREE_VIEW (gtk_builder_get_object (builder,
			"Friends List"));
	GtkTreeSelection *selection = gtk_tree_view_get_selection (view->friends_list);
	g_signal_connect (selection, "changed",
			G_CALLBACK (gsm_view_friends_list_selection_changed), view);
	
	cell = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (
			_("Nick name"), cell, "text", COL_FL_NAME, NULL);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_column_set_sort_column_id (column, COL_FL_NAME);
	gtk_tree_view_append_column (view->friends_list, column);
	
	cell = gtk_cell_renderer_toggle_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Notify"), cell,
			"active", COL_FL_NOTIFY, NULL);
	gtk_tree_view_column_set_sort_column_id (column, COL_FL_NOTIFY);
	gtk_tree_view_append_column (view->friends_list, column);
	
	GtkListStore *store = gtk_list_store_new (COL_FL_NUMS, G_TYPE_STRING,
			G_TYPE_BOOLEAN);
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), COL_FL_NAME,
			GTK_SORT_ASCENDING);
	gtk_tree_view_set_model (view->friends_list, GTK_TREE_MODEL (store));
	g_object_unref (store);
}

void G_MODULE_EXPORT
gsm_view_friends_list_add_icon_press (GtkEntry *entry,
		GtkEntryIconPosition icon_pos, GdkEvent *event, GsmView *view)
{
	if (icon_pos == GTK_ENTRY_ICON_SECONDARY) {
		g_signal_emit (view, signals[FRIEND_ADD], 0, gtk_entry_get_text (entry));
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	}
}

gboolean G_MODULE_EXPORT
gsm_view_friends_list_name_key_press (GtkEntry *entry, GdkEventKey *event,
		GsmView *view)
{
	if (event->keyval == GDK_Return) {
		g_signal_emit (view, signals[FRIEND_ADD], 0,
				gtk_entry_get_text (entry));
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	}
	
	return FALSE;
}

void G_MODULE_EXPORT
gsm_view_friends_list_name_changed (GtkEntry *entry, GsmView *view)
{
	gboolean exists = FALSE;
	const gchar* address = gtk_entry_get_text (entry);
	
	if (*address == 0)
		exists = TRUE;
	else
		g_signal_emit (view, signals[FRIEND_NAME_CHANGE], 0, address, &exists);
	
	if (exists)
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
	else
		gtk_entry_set_icon_sensitive (entry, GTK_ENTRY_ICON_SECONDARY, TRUE);
}

gboolean G_MODULE_EXPORT
gsm_view_friends_list_button_press (GtkWidget *widget, GdkEventButton *event,
		GsmView *view)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkTreePath *path;
	GtkTreeViewColumn *column;
	int cell_x, cell_y;
	gchar *name;
	
	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view->friends_list),
				event->x, event->y, &path, &column, &cell_x, &cell_y)) {
			
			if (strcmp (gtk_tree_view_column_get_title (column), _("Notify")) == 0) {
				gboolean enabled;
				model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->friends_list));
				gtk_tree_model_get_iter (model, &iter, path);
				gtk_tree_model_get (model, &iter,
						COL_FL_NAME, &name,
						COL_FL_NOTIFY, &enabled,
						-1);
				g_signal_emit (view, signals[FRIEND_UPDATE], 0, name, !enabled);
				g_free (name);
			}
			
			if (strcmp (gtk_tree_view_column_get_title (column), _("Remove")) == 0) {
				model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->friends_list));
				gtk_tree_model_get_iter (model, &iter, path);
				gtk_tree_model_get (model, &iter,
						COL_FL_NAME, &name,
						-1);
				
				GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (view),
						GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
						"Are you sure you want to remove %s from friends list?",
						name);
				if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
					g_signal_emit (view, signals[FRIEND_REMOVE], 0, name);
				gtk_widget_destroy (dialog);
				
				g_free (name);
			}
			
			gtk_tree_path_free (path);
		}
	}
	
	return FALSE;
}

static void
gsm_view_friends_list_remove (GsmView *view)
{
	GtkTreeModel *model;
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	gchar *name;
	
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->friends_list));
	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter, COL_FL_NAME, &name, -1);
		GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (view),
				GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
				"Are you sure you want to remove %s from list?", name);
		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
			g_signal_emit (view, signals[FRIEND_REMOVE], 0, name);
		gtk_widget_destroy (dialog);
		g_free (name);
	}
}

void G_MODULE_EXPORT
gsm_view_friends_list_remove_clicked (GtkButton *button, GsmView *view)
{
	gsm_view_friends_list_remove (view);
}

gboolean G_MODULE_EXPORT
gsm_view_friends_list_key_press (GtkWidget *widget, GdkEventKey *event,
		GsmView *view)
{
	if (event->keyval == GDK_Delete)
		gsm_view_friends_list_remove (view);
	return FALSE;
}

void
gsm_view_add_friend (GsmView *view, const gchar *name, gboolean notify)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->friends_list));
	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
			COL_FL_NAME, name,
			COL_FL_NOTIFY, notify,
			-1);
	
	if (gtk_tree_model_iter_n_children (model, NULL) == 1) {
		GtkTreeSelection *sel = gtk_tree_view_get_selection (view->friends_list);
		gtk_tree_selection_select_iter (sel, &iter);
	}
}

void
gsm_view_remove_friend (GsmView *view, const gchar *name)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	gchar *n;
	
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->friends_list));
	
	if (gtk_tree_model_get_iter_first (model, &iter)) {
		do {
			gtk_tree_model_get (model, &iter, COL_FL_NAME, &n, -1);
			if (strcmp (n, name) == 0) {
				gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
				g_free (n);
				return;
			}
			g_free (n);
		} while (gtk_tree_model_iter_next (model, &iter));
	}
}

void
gsm_view_update_friend (GsmView *view, const gchar *name, const gchar *address,
		gboolean notify)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	gchar *n;
	
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->friends_list));
	
	if (gtk_tree_model_get_iter_first (model, &iter)) {
		do {
			gtk_tree_model_get (model, &iter, COL_FL_NAME, &n, -1);
			if (strcmp (n, name) == 0)
				gtk_list_store_set (GTK_LIST_STORE (model), &iter,
						COL_FL_NAME, name,
						COL_FL_NOTIFY, notify,
						-1);
			g_free (n);
		} while (gtk_tree_model_iter_next (model, &iter));
	}
}


// Preferences   ===============================================================

static void
gsm_view_prefs_init (GsmView *view, GtkBuilder *builder)
{
	view->prefs_update_rate = GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
			"Update Rate"));
	view->prefs_sound = GTK_FILE_CHOOSER_BUTTON (gtk_builder_get_object (builder,
			"Notification Sound"));
}

void G_MODULE_EXPORT
gsm_view_prefs_update_rate_changed (GtkEditable *editable, GsmView *view)
{
	gdouble d = gtk_spin_button_get_value (view->prefs_update_rate);
	glong l = d * 1000;
	g_signal_emit (view, signals[PREFERENCE_CHANGE], 0, "update-rate", l);
}

void G_MODULE_EXPORT
gsm_view_prefs_sound_changed (GtkFileChooserButton *chooser, GsmView *view)
{
	gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
	g_signal_emit (view, signals[PREFERENCE_CHANGE], 0, "notification-sound", fname);
}


// =============================================================================

void
gsm_view_add_server (GsmView *view, GsmWatcher *watcher)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	
	model = gtk_tree_view_get_model (view->servers_list);
	
	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
			COL_SL_NAME, watcher->name,
			COL_SL_WATCHER, watcher,
			-1);
	
	if (gtk_tree_model_iter_n_children (model, NULL) == 1) {
		GtkTreeSelection *sel = gtk_tree_view_get_selection (view->servers_list);
		gtk_tree_selection_select_iter (sel, &iter);
	}
	
	g_object_set_data (G_OBJECT (watcher), "tree-iter",
			gtk_tree_iter_copy (&iter));
}

void
gsm_view_remove_server (GsmView *view, GsmWatcher *watcher)
{
	GtkTreeModel *model;
	GtkTreeIter *iter;
	
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->servers_list));
	iter = g_object_get_data (G_OBJECT (watcher), "tree-iter");
	gtk_list_store_remove (GTK_LIST_STORE (model), iter);
	gtk_tree_iter_free (iter);
}

static GdkPixbuf *
get_game_icon (const gchar *name)
{
	static GHashTable *icons = NULL;
	gchar *path;
	GdkPixbuf *pixbuf;
	
	if (!name)
		return NULL;
	
	if (!icons)
		icons = g_hash_table_new (g_str_hash, g_str_equal);
	
	if ((pixbuf = g_hash_table_lookup (icons, name)))
		return pixbuf;
	
	path = g_build_filename (GSM_PATH_ICONS, name, NULL);
	pixbuf = gdk_pixbuf_new_from_file (path, NULL);
	g_free (path);
	g_hash_table_insert (icons, (gpointer) name, pixbuf);
	return pixbuf;
}

void
gsm_view_update_server (GsmView *view, GsmWatcher *watcher)
{
	GtkTreeModel *model;
	GtkTreeIter *iter;
	gchar ping_txt[16];
	gchar players_txt[16];
	gchar *players_color;
	
	g_sprintf (players_txt, "%d / %d", watcher->num_players, watcher->max_players);
	players_color = watcher->num_players == 0 ? "black" :
			(watcher->num_players >= watcher->max_players ? "dark red" : "dark green");
	
	g_sprintf (ping_txt, "%lu", watcher->ping);
	
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->servers_list));
	iter = g_object_get_data (G_OBJECT (watcher), "tree-iter");
	
	gtk_list_store_set (GTK_LIST_STORE (model), iter,
			COL_SL_NAME, watcher->name,
			COL_SL_GAME_ICON, get_game_icon (watcher->icon),
			COL_SL_GAME, watcher->game,
			COL_SL_MAP, watcher->map,
			COL_SL_PLAYERS, players_txt,
			COL_SL_PLAYERS_COLOR, players_color,
			COL_SL_PING, ping_txt,
			COL_SL_PING_COLOR, NULL,
			-1);
	
	if (view->selected == watcher) {
		gsm_view_server_info_update (view, watcher);
		gsm_view_players_list_update (view, watcher);
	}
}

void
gsm_view_error (GsmView *view, GsmWatcher *watcher, GError *error)
{
	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (view->servers_list));
	GtkTreeIter *iter = g_object_get_data (G_OBJECT (watcher), "tree-iter");
	
	gtk_list_store_set (GTK_LIST_STORE (model), iter,
			COL_SL_PING, "Timeout",
			COL_SL_PING_COLOR, "red",
			-1);
}

void
gsm_view_show (GsmView *view)
{
	g_return_if_fail (GSM_IS_VIEW (view));
	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view->tray_menu_show),
			TRUE);
}

void
gsm_view_hide (GsmView *view)
{
	g_return_if_fail (GSM_IS_VIEW (view));
	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view->tray_menu_show),
			FALSE);
}

void
gsm_view_set_update_rate (GsmView *view, glong rate)
{
	g_return_if_fail (GSM_IS_VIEW (view));
	gtk_spin_button_set_value (view->prefs_update_rate, (gdouble)rate / 1000);
}

void
gsm_view_set_notification_sound (GsmView *view, gchar *fname)
{
	g_return_if_fail (GSM_IS_VIEW (view));
	gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (view->prefs_sound), fname);
}